package cn.bandeqing.finance.rpc.impl;

import cn.bandeqing.base.domain.AdminUserStore;
import cn.bandeqing.base.domain.CurrentAdminUser;
import cn.bandeqing.base.resp.Resp;
import cn.bandeqing.base.resp.Table;
import cn.bandeqing.finance.req.*;
import cn.bandeqing.finance.resp.*;
import cn.bandeqing.finance.rpc.IFinanceRpc;
import cn.bandeqing.frame.exception.CodeException;
import cn.bandeqing.order.entity.Order;
import cn.bandeqing.order.service.IOrderService;
import cn.bandeqing.orderCost.entity.OcType;
import cn.bandeqing.orderCost.entity.OrderCost;
import cn.bandeqing.orderCost.entity.UsSplitType;
import cn.bandeqing.orderCost.service.IOrderCostService;
import cn.bandeqing.orderWorker.entity.OwStatus;
import cn.bandeqing.store.entity.Store;
import cn.bandeqing.store.service.IStoreService;
import cn.bandeqing.stp.StpAdminUtil;
import cn.bandeqing.userWallet.entity.UserWallet;
import cn.bandeqing.userWallet.service.IUserWalletService;
import cn.bandeqing.userWalletRecord.entity.UserWalletRecord;
import cn.bandeqing.userWalletRecord.entity.UwrStatus;
import cn.bandeqing.userWalletRecord.entity.UwrType;
import cn.bandeqing.userWalletRecord.service.IUserWalletRecordService;
import cn.bandeqing.worker.service.IWorkerService;
import cn.bandeqing.workerWallet.entity.WorkerWallet;
import cn.bandeqing.workerWallet.service.IWorkerWalletService;
import cn.bandeqing.workerWalletRecord.entity.WorkerWalletRecord;
import cn.bandeqing.workerWalletRecord.entity.WwrStatus;
import cn.bandeqing.workerWalletRecord.entity.WwrType;
import cn.bandeqing.workerWalletRecord.service.IWorkerWalletRecordService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import io.github.linpeilie.Converter;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.ObjUtil;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import static cn.bandeqing.order.entity.table.OrderTableDef.ORDER;
import static cn.bandeqing.orderCost.entity.table.OrderCostTableDef.ORDER_COST;
import static cn.bandeqing.orderWorker.entity.table.OrderWorkerTableDef.ORDER_WORKER;
import static cn.bandeqing.userWallet.entity.table.UserWalletTableDef.USER_WALLET;
import static cn.bandeqing.userWalletRecord.entity.table.UserWalletRecordTableDef.USER_WALLET_RECORD;
import static cn.bandeqing.worker.entity.table.WorkerTableDef.WORKER;
import static cn.bandeqing.workerWallet.entity.table.WorkerWalletTableDef.WORKER_WALLET;
import static cn.bandeqing.workerWalletRecord.entity.table.WorkerWalletRecordTableDef.WORKER_WALLET_RECORD;

@DubboService
public class FinanceRpcImpl implements IFinanceRpc {

    @Resource
    private IOrderService orderService;
    @Resource
    private IOrderCostService orderCostService;
    @Resource
    private IWorkerService workerService;
    @Resource
    private IWorkerWalletService workerWalletService;
    @Resource
    private IWorkerWalletRecordService workerWalletRecordService;
    @Resource
    private IStoreService storeService;
    @Resource
    private IUserWalletService userWalletService;
    @Resource
    private IUserWalletRecordService userWalletRecordService;
    @Resource
    private Converter converter;

    @Override
    public Resp<Table<FinanceOrderTable>> table(FinanceOrderTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(ORDER.ORDER_ID.eq(req.getOrderId()))
                .where(ORDER.SERVICE_TIME.ge(req.getServiceTimeStart()))
                .where(ORDER.CREATE_TIME.ge(req.getCreateTimeStart()))
                .where(ORDER.CREATE_TIME.le(req.getCreateTimeEnd()))
                .where(ORDER.SERVICE_TIME.le(req.getServiceTimeEnd()))
                .where(ORDER.CUSTOMER_NAME.eq(req.getCustomerName()))
                .where(ORDER.CUSTOMER_PHONE.eq(req.getCustomerPhone()))
                .where(ORDER.START_ADDRESS.eq(req.getStartAddress()))
                .where(ORDER.NEW_ADDRESS.eq(req.getNewAddress()))
                .where(ORDER.HAND_IN_TIME.isNotNull(req.getHandInTimeStatus() != null && req.getHandInTimeStatus() == 1))
                .where(ORDER.ORDER_STATUS.eq(req.getOrderStatus()))
                .where(ORDER.HAND_IN_TIME.ge(req.getHandInTimeStart()))
                .where(ORDER.HAND_IN_TIME.le(req.getHandInTimeEnd()))
                .where(ORDER.WRITE_OFF_TIME.isNotNull(req.getWriteOffTimeStatus() != null && req.getWriteOffTimeStatus() == 1))
                .where(ORDER.WRITE_OFF_TIME.ge(req.getWriteOffTimeStart()))
                .where(ORDER.WRITE_OFF_TIME.le(req.getWriteOffTimeEnd()))
                .where(ORDER.STORE_ID.eq(req.getStoreId()))
                .orderBy(ORDER.ORDER_ID.desc());

        if (req.getStoreId() == null) {
            CurrentAdminUser current = StpAdminUtil.getCurrent();
            if (current.checkStoreAccount()) {
                // 店铺账号 只能查询店铺下的权限
                qw.where(ORDER.STORE_ID.in(current.getStores().stream().map(AdminUserStore::getStoreId).toList()));
            }
        }

        Page<FinanceOrderTable> page = orderService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, FinanceOrderTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    @Transactional
    public synchronized Resp<Void> handIn(HandInReq req) {
        long count = orderService.count(QueryWrapper.create()
                .where(ORDER.HAND_IN_TIME.isNotNull())
                .where(ORDER.ORDER_ID.in(req.getOrderIds())));
        if (count > 0) {
            return Resp.error("有已完成交款的订单，请刷新后重试");
        }
        CurrentAdminUser current = StpAdminUtil.getCurrent();
        boolean update = orderService.update(Order.builder()
                .handInTime(new Timestamp(System.currentTimeMillis()))
                .handInUserId(current.getUsId())
                .handInUserName(current.getUsNickName())
                .build(), QueryWrapper.create()
                .where(ORDER.ORDER_ID.in(req.getOrderIds())));
        Assert.isTrue(update, () -> new CodeException("交款操作失败"));
        return Resp.success();
    }

    @Override
    @Transactional
    public synchronized Resp<Void> writeOff(WriteOffReq req) {
        long count = orderService.count(QueryWrapper.create()
                .where(ORDER.WRITE_OFF_TIME.isNotNull())
                .where(ORDER.ORDER_ID.in(req.getOrderIds())));
        if (count > 0) {
            return Resp.error("有已完成核销的订单，请刷新后重试");
        }

        List<OrderCost> orderCosts = orderCostService.list(QueryWrapper.create()
                .where(ORDER_COST.OC_SETTLEMENT_TIME.isNull())
                .where(ORDER_COST.ORDER_ID.in(req.getOrderIds())));

        // 不是分成 工作人员工费
        List<Long> workids = orderCosts.stream().filter(oc -> !OcType.split.equals(oc.getOcType())).map(OrderCost::getWorkerId).toList();
        List<WorkerWallet> workerWallets = workerWalletService.list(QueryWrapper.create()
                .select(WORKER_WALLET.WORKER_ID,
                        WORKER_WALLET.WORKER_BALANCE,
                        WORKER_WALLET.WORKER_NICK_NAME)
                .where(WORKER_WALLET.WORKER_ID.in(workids)));

        List<WorkerWalletRecord> workerWalletRecords = new ArrayList<>();

        for (OrderCost oc : orderCosts) {
            workerWallets.stream().filter(ww -> ww.getWorkerId().equals(oc.getWorkerId())).findFirst().ifPresent(ww -> {
                Double before = ww.getWorkerBalance();
                Double money = oc.getOcMoney();
                Double after = before + money;

                ww.setWorkerBalance(after);

                workerWalletRecords.add(WorkerWalletRecord.builder()
                        .workerId(oc.getWorkerId())
                        .workerNickName(oc.getWorkerNickName())
                        .orderId(oc.getOrderId())
                        .wwrType(WwrType.cost)
                        .wwrDesc(oc.getOcDesc())
                        .beforeMoney(before)
                        .changeMoney(money)
                        .afterMoney(after)
                        .ocId(oc.getOcId())
//                        .wwrStatus(WwrStatus.pass)
                        .build());
            });
        }

        if (!workerWalletRecords.isEmpty()) {
            boolean b1 = workerWalletService.updateBatch(workerWallets);
            Assert.isTrue(b1, () -> new CodeException("钱包批量更新失败"));

            boolean b = workerWalletRecordService.saveBatch(workerWalletRecords);
            Assert.isTrue(b, () -> new CodeException("钱包记录批量保存失败"));
        }

        // 分成
        List<Long> usids = orderCosts.stream().filter(oc -> OcType.split.equals(oc.getOcType())).map(OrderCost::getUsId).toList();
        List<UserWallet> userWallets = userWalletService.list(QueryWrapper.create()
                .select(USER_WALLET.US_ID,
                        USER_WALLET.US_BALANCE,
                        USER_WALLET.US_NICK_NAME)
                .where(USER_WALLET.US_ID.in(usids)));

        List<UserWalletRecord> userWalletRecords = new ArrayList<>();

        for (OrderCost oc : orderCosts) {
            userWallets.stream().filter(ww -> ww.getUsId().equals(oc.getUsId())).findFirst().ifPresent(uw -> {
                Double before = uw.getUsBalance();
                Double money = oc.getOcMoney();
                Double after = before + money;

                uw.setUsBalance(after);

                userWalletRecords.add(UserWalletRecord.builder()
                        .usId(oc.getUsId())
                        .usNickName(oc.getUsNickName())
                        .orderId(oc.getOrderId())
                        .uwrType(UwrType.split)
                        .uwrDesc(oc.getOcDesc())
                        .beforeMoney(before)
                        .changeMoney(money)
                        .afterMoney(after)
                        .ocId(oc.getOcId())
                        .build());
            });
        }

        if (!userWalletRecords.isEmpty()) {
            boolean b2 = userWalletService.updateBatch(userWallets);
            Assert.isTrue(b2, () -> new CodeException("钱包批量更新失败"));

            boolean b3 = userWalletRecordService.saveBatch(userWalletRecords);
            Assert.isTrue(b3, () -> new CodeException("钱包记录批量保存失败"));
        }

        CurrentAdminUser current = StpAdminUtil.getCurrent();

        List<Long> ocIds = orderCosts.stream().map(OrderCost::getOcId).toList();
        boolean update1 = orderCostService.update(OrderCost.builder()
                .ocSettlementTime(new Timestamp(System.currentTimeMillis()))
                .ocSettlementUserName(current.getUsNickName())
                .ocSettlementUserId(current.getUsId())
                .build(), QueryWrapper.create()
                .where(ORDER_COST.OC_SETTLEMENT_TIME.isNull())
                .where(ORDER_COST.OC_ID.in(ocIds)));
        Assert.isTrue(update1, () -> new CodeException("成本核销失败"));

        boolean update = orderService.update(Order.builder()
                .writeOffTime(new Timestamp(System.currentTimeMillis()))
                .writeOffUserId(current.getUsId())
                .writeOffUserName(current.getUsNickName())
                .build(), QueryWrapper.create()
                .where(ORDER.WRITE_OFF_TIME.isNull())
                .where(ORDER.ORDER_ID.in(req.getOrderIds())));
        Assert.isTrue(update, () -> new CodeException("订单核销操作失败"));
        return Resp.success();
    }

    @Override
    public Resp<Table<FinanceOrderCostTable>> costTable(FinanceOrderCostTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(ORDER_COST.ORDER_ID.eq(req.getOrderId()))
                .orderBy(ORDER_COST.OC_ID.desc());
        List<FinanceOrderCostTable> financeOrderCostTables = orderCostService.listAs(qw, FinanceOrderCostTable.class);
        return Resp.success(Table.of(financeOrderCostTables, (long) financeOrderCostTables.size()));
    }

    @Override
    public void costSaveOrUpdate(FinanceCostSaveOrUpdateReq req) {
        OrderCost oc = converter.convert(req, OrderCost.class);
        boolean b = orderCostService.saveOrUpdate(oc);
        Assert.isTrue(b, CodeException::new);
    }

    @Override
    @Transactional
    public void costSettlement(FinanceCostSettlementReq req) {
        OrderCost oc = orderCostService.getById(req.getOcId());
        if (OcType.split.equals(oc.getOcType())) {
            // 分成
            Assert.notNull(oc.getUsId(), () -> new CodeException("分成未选择用户无法核销"));
            userWalletService.changeBalance(oc.getUsId(), oc.getUsNickName(), oc.getOcMoney(), oc.getOcDesc(), oc.getOcId(), oc.getOrderId(), UwrType.split);
        } else {
            // 暂定工费有关
            Assert.notNull(oc.getWorkerId(), () -> new CodeException("工费有关 工作人员未选择无法核销"));
            workerWalletService.changeBalance(oc.getWorkerId(), oc.getWorkerNickName(), oc.getOcMoney(), oc.getOcDesc(), oc.getOcId(), oc.getOrderId(), WwrType.cost);
        }

        CurrentAdminUser current = StpAdminUtil.getCurrent();
        boolean b = orderCostService.updateById(OrderCost.builder()
                .ocId(req.getOcId())
                .ocSettlementTime(new Timestamp(System.currentTimeMillis()))
                .ocSettlementUserId(current.getUsId())
                .ocSettlementUserName(current.getUsNickName())
                .build());
        Assert.isTrue(b, CodeException::new);
    }

    @Override
    public Resp<List<LabelValue>> workerSelect(FinanceWorkerSelectReq req) {
        List<LabelValue> labelValues = workerService.listAs(QueryWrapper.create()
                .select(WORKER.WORKER_NICK_NAME.as(LabelValue::getLabel),
                        WORKER.WORKER_ID.as(LabelValue::getValue))
                .innerJoin(ORDER_WORKER).on(ORDER_WORKER.WORKER_ID.eq(WORKER.WORKER_ID))
                .where(ORDER_WORKER.OW_STATUS.in(OwStatus.worker_accept, OwStatus.erp_accept))
                .where(ORDER_WORKER.ORDER_ID.eq(req.getOrderId())), LabelValue.class);
        return Resp.success(labelValues);
    }

    @Override
    public Resp<Void> costRemove(FinanceCostRemoveReq req) {
        boolean b = orderCostService.remove(QueryWrapper.create()
                .where(ORDER_COST.OC_SETTLEMENT_TIME.isNull())
                .where(ORDER_COST.OC_ID.eq(req.getOcId())));
        Assert.isTrue(b, () -> new CodeException("删除失败，可能是成本已经核销或已经删除，请刷新后重试"));
        return Resp.success();
    }

    @Override
    public Resp<List<LabelValue>> userSelect(FinanceWorkerSelectReq req) {
        Order order = orderService.getOne(QueryWrapper.create().select(ORDER.STORE_ID).where(ORDER.ORDER_ID.eq(req.getOrderId())));
        if (ObjUtil.isNull(order) || ObjUtil.isNull(order.getStoreId())) {
            return Resp.success();
        }
        Store store = storeService.getById(order.getStoreId());
        ArrayList<LabelValue> labelValues = new ArrayList<>();
        if (ObjUtil.isNotNull(store)) {
            if (UsSplitType.city_partner.getValue().equals(req.getUsSplitType())) {
                if (ObjUtil.isNotNull(store.getCityPartnerUsId())) {
                    labelValues.add(new LabelValue(store.getCityPartnerUsNickName(), store.getCityPartnerUsId()));
                }
            }
            if (UsSplitType.shopkeeper.getValue().equals(req.getUsSplitType())) {
                if (ObjUtil.isNotNull(store.getShopkeeperUsId())) {
                    labelValues.add(new LabelValue(store.getShopkeeperUsNickName(), store.getShopkeeperUsId()));
                }
            }
            if (UsSplitType.store_manager.getValue().equals(req.getUsSplitType())) {
                if (ObjUtil.isNotNull(store.getStoreManagerUsId())) {
                    labelValues.add(new LabelValue(store.getStoreManagerUsNickName(), store.getStoreManagerUsId()));
                }
            }
        }
        return Resp.success(labelValues);
    }

    @Override
    public Resp<Table<FinanceWorkerWalletTable>> workerWallteTable(FinanceWorkerWalletTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(WORKER_WALLET.WORKER_ID.eq(req.getWorkerId()));
        Page<FinanceWorkerWalletTable> page = workerWalletService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, FinanceWorkerWalletTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    @Transactional
    public synchronized Resp<Void> workerWallteChange(FinanceWorkerWalletChangeReq req) {
        WorkerWalletRecord wwr = converter.convert(req, WorkerWalletRecord.class);
        WorkerWallet ww = workerWalletService.getById(req.getWorkerId());
        Double before = ww.getWorkerBalance();
        Double after = before + req.getChangeMoney();
        ww.setWorkerBalance(after);
        boolean b = workerWalletService.updateById(ww);
        Assert.isTrue(b, () -> new CodeException("钱包余额更新失败"));

        wwr.setBeforeMoney(before);
        wwr.setAfterMoney(after);
        wwr.setWorkerNickName(ww.getWorkerNickName());
//        wwr.setWwrStatus(WwrStatus.pass);
//        wwr.setAccountType();

        boolean save = workerWalletRecordService.save(wwr);
        Assert.isTrue(save, () -> new CodeException("钱包记录保存失败"));

        return Resp.success();
    }

    @Override
    public Resp<Table<FinanceWorkerWalletRecordTable>> workerWallteRecordTable(FinanceWorkerWalletRecordTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(WORKER_WALLET_RECORD.WORKER_ID.eq(req.getWorkerId()))
                .orderBy(WORKER_WALLET_RECORD.WWR_ID.desc());
        Page<FinanceWorkerWalletRecordTable> page = workerWalletRecordService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, FinanceWorkerWalletRecordTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    public Resp<Table<FinanceUserWalletTable>> userWallteTable(FinanceUserWalletTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(USER_WALLET.US_ID.eq(req.getUsId()));
        Page<FinanceUserWalletTable> page = userWalletService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, FinanceUserWalletTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    @Transactional
    public synchronized Resp<Void> userWallteChange(FinanceUserWalletChangeReq req) {
        UserWalletRecord wwr = converter.convert(req, UserWalletRecord.class);
        UserWallet uw = userWalletService.getById(req.getUsId());
        Double before = uw.getUsBalance();
        Double after = before + req.getChangeMoney();
        uw.setUsBalance(after);
        boolean b = userWalletService.updateById(uw);
        Assert.isTrue(b, () -> new CodeException("用户钱包余额更新失败"));

        wwr.setBeforeMoney(before);
        wwr.setAfterMoney(after);
        wwr.setUsNickName(uw.getUsNickName());
        boolean save = userWalletRecordService.save(wwr);
        Assert.isTrue(save, () -> new CodeException("用户钱包记录保存失败"));

        return Resp.success();
    }

    @Override
    public Resp<Table<FinanceUserWalletRecordTable>> userWallteRecordTable(FinanceUserWalletRecordTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(USER_WALLET_RECORD.US_ID.eq(req.getUsId()))
                .orderBy(USER_WALLET_RECORD.UWR_ID.desc());
        Page<FinanceUserWalletRecordTable> page = userWalletRecordService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, FinanceUserWalletRecordTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    public Resp<Void> checkWorkerWithdraw(CheckWorkerWithdrawReq req) {
        WorkerWalletRecord wwr = converter.convert(req, WorkerWalletRecord.class);
        CurrentAdminUser current = StpAdminUtil.getCurrent();

        if (WwrStatus.pass.equals(wwr.getWwrStatus())) {
            wwr.setPassUserId(current.getUsId());
            wwr.setPassUserName(current.getUsNickName());
            wwr.setPassTime(new Timestamp(System.currentTimeMillis()));
            wwr.setPassRemark(req.getRemark());
        } else if (WwrStatus.reject.equals(wwr.getWwrStatus())) {
            wwr.setRejectUserId(current.getUsId());
            wwr.setRejectUserName(current.getUsNickName());
            wwr.setRejectTime(new Timestamp(System.currentTimeMillis()));
            wwr.setRejectRemark(req.getRemark());
        }

        Assert.isTrue(workerWalletRecordService.updateById(wwr), CodeException::new);
        return Resp.success();
    }

    @Override
    public Resp<Void> accountWorkerWithdraw(AccountWorkerWithdrawReq req) {
        WorkerWalletRecord wwr = converter.convert(req, WorkerWalletRecord.class);
        CurrentAdminUser current = StpAdminUtil.getCurrent();
        wwr.setAccountUserId(current.getUsId());
        wwr.setAccountUserName(current.getUsNickName());
        wwr.setAccountTime(new Timestamp(System.currentTimeMillis()));

        Assert.isTrue(workerWalletRecordService.updateById(wwr), CodeException::new);

        return Resp.success();
    }

    @Override
    public Resp<Void> checkUserWithdraw(CheckUserWithdrawReq req) {
        UserWalletRecord uwr = converter.convert(req, UserWalletRecord.class);
        CurrentAdminUser current = StpAdminUtil.getCurrent();

        if (UwrStatus.pass.equals(uwr.getUwrStatus())) {
            uwr.setPassUserId(current.getUsId());
            uwr.setPassUserName(current.getUsNickName());
            uwr.setPassTime(new Timestamp(System.currentTimeMillis()));
            uwr.setPassRemark(req.getRemark());
        } else if (UwrStatus.reject.equals(uwr.getUwrStatus())) {
            uwr.setRejectUserId(current.getUsId());
            uwr.setRejectUserName(current.getUsNickName());
            uwr.setRejectTime(new Timestamp(System.currentTimeMillis()));
            uwr.setRejectRemark(req.getRemark());
        }

        Assert.isTrue(userWalletRecordService.updateById(uwr), CodeException::new);
        return Resp.success();
    }

    @Override
    public Resp<Void> accountUserWithdraw(AccountUserWithdrawReq req) {
        UserWalletRecord uwr = converter.convert(req, UserWalletRecord.class);
        CurrentAdminUser current = StpAdminUtil.getCurrent();
        uwr.setAccountUserId(current.getUsId());
        uwr.setAccountUserName(current.getUsNickName());
        uwr.setAccountTime(new Timestamp(System.currentTimeMillis()));

        Assert.isTrue(userWalletRecordService.updateById(uwr), CodeException::new);

        return Resp.success();
    }

}
